<!--  -->
<template>
  <div class="app-container">
    <!-- START 卡片视图 START -->
    <el-card style="height: 80px; margin-bottom: 10px;">
      <!-- START 搜索与添加按扭区域 START -->
      <el-row :gutter="20">
        <el-col :span="8">
          <el-input
            v-model="queryInfo.roleName"
            placeholder="请输入要搜索的角色名"
            clearable
            @clear="getRolesList"
          >
            <el-button
              slot="append"
              icon="el-icon-search"
              @click="getRolesList"
            />
          </el-input>
        </el-col>
        <el-col :span="4">
          <el-button v-hasPermi="['/system/role/saveRole']" type="primary" icon="el-icon-circle-plus-outline" @click="openRoleDialog">添加角色</el-button>
        </el-col>
      </el-row>
      <!--  END 搜索与添加按扭区域 END -->
    </el-card>
    <!-- END 卡片视图 END -->

    <!-- START 角色列表区域 START -->
    <el-card>
      <el-table v-loading="listLoading" :data="roleList" :stripe="true" :border="true" style="width: 100%" @sort-change="sortChange">
        <!-- type="expand" 可展开式的箭头，实现点击这个箭头，将当前角色所拥有的权限列表展开 -->
        <el-table-column type="expand">
          <template slot-scope="scope">
            <!-- {{ scope.row }} 得到当前角色对象 scope.row.children得到当前角色下的权限列表 -->
            <el-row
              v-for="(item1, idx1) in scope.row.children"
              :key="idx1"
              :class="['border-bottom', idx1 === 0 ? 'border-top' : 'vcenter']"
            >
              <!-- 渲染一级权限 -->
              <el-col :span="5">
                <el-tag effect="dark" type="primary" closable @close="removeRoleThreeRight(scope.row, item1.id)">{{ item1.name }}</el-tag>
                <i class="el-icon-caret-right" />
              </el-col>
              <!-- 渲染二级和三级权限 -->
              <el-col :span="19">
                <!-- START 通过v-for循环嵌套渲染二级权限 START -->
                <el-row v-for="(item2, idx2) in item1.children" :key="item2.id" :class="['border-bottom', idx2 === 0 ? '' : 'border-top']">
                  <el-col :span="6">
                    <el-tag effect="dark" type="success" closable @close="removeRoleThreeRight(scope.row, item2.id)">{{ item2.name }}</el-tag>
                    <i class="el-icon-caret-right" />
                  </el-col>
                  <!-- 渲染三级权限 -->
                  <el-col :span="18">
                    <el-tag
                      v-for="(item3, idx3) in item2.children"
                      :key="item3.id"
                      effect="dark"
                      type="warning"
                      closable
                      @close="removeRoleThreeRight(scope.row, item3.id)"
                    >{{ item3.name }}</el-tag>
                  </el-col>
                </el-row>
              </el-col>
            </el-row>
          </template>
        </el-table-column>
        <el-table-column label="ID" prop="id" sortable="custom" align="center" width="60">
          <template slot-scope="{row}">
            <span>{{ row.id }}</span>
          </template>
        </el-table-column>
        <el-table-column label="英文名称" width="300px" align="center">
          <template slot-scope="{row}">
            <span>{{ row.name }}</span>
          </template>
        </el-table-column>
        <el-table-column label="中文名称" width="300px" align="center">
          <template slot-scope="{row}">
            <span>{{ row.nameZh }}</span>
          </template>
        </el-table-column>
        <el-table-column label="操作" align="center">
          <!-- 使用作用域插槽的形式为[角色等级]列自定义内容，使用scope来接收所有的数据 -->
          <template slot-scope="scope">
            <!-- 编辑按扭 -->
            <el-button v-hasPermi="['/system/role/updateRole']" icon="el-icon-edit" type="primary" size="mini" plain round @click="showEditDialog(scope.row)">编辑</el-button>
            <!-- 删除按扭 -->
            <el-button v-hasPermi="['/system/role/delRole']" icon="el-icon-delete" type="danger" plain round size="mini" @click="delRoleById(scope.row)">删除</el-button>
            <!-- 分配权限按扭 -->
            <el-button v-hasPermi="['/system/role/assignMenu']" icon="el-icon-setting" type="success" size="mini" plain round @click="assignRightToRole(scope.row)">分配权限</el-button>
          </template>
        </el-table-column>
      </el-table>

      <!-- START 分页区 START -->
      <pagination v-show="totalCount>0" :total="totalCount" :page.sync="queryInfo.currPageNo" :limit.sync="queryInfo.pageSize" @pagination="getRolesList" />
      <!-- END 分页区 END -->

    </el-card>
    <!-- END 权限列表区域 END -->

    <!-- START 添加角色弹出对话框窗口 START -->
    <el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible" width="450px">
      <el-form ref="dataFormRef" :rules="rules" :model="role" label-position="left" label-width="100px" style="width: 360px; margin-left:20px;">
        <el-form-item label="英文名" prop="name">
          <el-input v-model="role.name" placeholder="角色英文名" />
        </el-form-item>
        <el-form-item label="中文名" prop="nameZh">
          <el-input v-model="role.nameZh" placeholder="角色中文名" />
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="cancelEvent()">
          取消
        </el-button>
        <el-button type="primary" @click="dialogStatus==='create'?insertData():updateData()">
          确认
        </el-button>
      </div>
    </el-dialog>
    <!-- END 添加角色弹出对话框窗口 END -->

    <!-- START 分配权限 START -->
    <el-dialog title="分配权限" :visible.sync="assignRightVisible" width="50%">
      <!-- START 主体内容部分——权限树形控件 START -->
      <!-- :default-expanded-keys="[2, 3]" :default-checked-keys="[5]" -->
      <el-tree
        ref="assignRightRef"
        :data="treeRightList"
        show-checkbox
        node-key="id"
        :default-checked-keys="defaultCheckedKeys"
        :props="defaultProps"
      />
      <!-- END 主体内容部分——权限树形控件 END -->
      <span slot="footer" class="dialog-footer">
        <el-button @click="assignRightVisible = false">取 消</el-button>
        <el-button type="primary" @click="saveAssignMenu">确 定</el-button>
      </span>
      <span slot="footer" class="dialog-footer" /></el-dialog>
    <!-- END 分配权限 END -->
  </div>
</template>

<script>
// 这里可以导入其他文件（比如：组件，工具js，第三方插件js，json文件，图片文件等等）
// 例如：import 《组件名称》 from '《组件路径》';
// 从 /api/role.js中解构出 pageRoleList方法，在当前组件中导入
import { pageRoleList, delMenuByRoleId, saveRole, findByRoleId, updateRole, assignMenu, delRoleByRoleId } from '@/api/role'
import { getAllMenuList, getMenuIdsByRoleId } from '@/api/menu'
// 导入分页公共组件
import Pagination from '@/components/Pagination'
export default {
// import引入的组件需要注入到对象中才能使用
  components: {
    // 在当前组件中进行注册
    Pagination
  },
  // props实现父子组件传递数据
  props: {},
  data() {
    // 这里存放数据
    return {
      // 查询条件
      queryInfo: {
        roleName: '', // 角色名
        currPageNo: 1, // 当前页码
        pageSize: 4, // 每页显示5条数据
        // 排序字段
        sort: '+id'
      },
      // 角色列表
      roleList: [],
      // 用来保存分页时所需要的总记录数
      totalCount: 0, // 当发送异步请求用户列表数据时设置它为true,可出现正在加载中
      listLoading: true,
      // 用于控制添加/修改角色对话框的显示和隐藏
      dialogFormVisible: false,
      // 因为添加和编辑用户共用一个弹出窗口，通过dialogStatus来动态设置弹出窗口的标题。
      dialogStatus: '',
      textMap: {
        create: '添加角色',
        update: '编辑角色'
      },
      // 用于绑定[添加/编辑角色对话框]里的表单数据
      role: {
        id: undefined, // 角色ID
        name: '', // 角色的英文名
        nameZh: '' // 角色的中文名
      },
      // 添加表单验证规则对象
      rules: {
        name: [
          { required: true, message: '角色英文名必填', trigger: 'blur' },
          { min: 2, max: 30, message: '姓名的长度在2~30个字符之间', trigger: 'blur' }
        ],
        nameZh: [
          { required: true, message: '角色中文名必填', trigger: 'blur' },
          { min: 2, max: 15, message: '姓名的长度在2~15个字符之间', trigger: 'blur' }
        ]
      },
      defaultProps: {
        // children作为子节点
        children: 'children',
        // 以菜单名称作为显示文本值
        label: 'name'
      },
      // 用于绑定分配权限对话框中树状权限的数据源
      treeRightList: [],
      // 分配权限对话框中树状态权限默认选中的Key，即权限id
      defaultCheckedKeys: [],
      // 控制[分配权限对话框]的显示与隐藏
      assignRightVisible: false,
      rid: 0
    }
  },
  // 监听属性 类似于data概念
  computed: {},
  // 监控data中的数据变化
  watch: {},
  // 生命周期 - 创建完成（可以访问当前this实例）
  created() {
    this.getRolesList()
  },
  // 生命周期 - 挂载完成（可以访问DOM元素）
  mounted() {

  },
  beforeCreate() {
  }, // 生命周期 - 创建之前
  beforeMount() {
  }, // 生命周期 - 挂载之前
  beforeUpdate() {
  }, // 生命周期 - 更新之前
  updated() {
  }, // 生命周期 - 更新之后
  beforeDestroy() {
  }, // 生命周期 - 销毁之前
  destroyed() {
  }, // 生命周期 - 销毁完成
  activated() {
  },
  // 方法集合
  methods: {
    // 删除按扭的点击事件
    delRoleById(role) {
      this.$confirm('确认要删除此角色信息吗?', {
        confirmButtonText: '确认',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        delRoleByRoleId(role.id).then((res) => {
          if (res.code !== 200) return this.$message.error(res.message)
          this.$message.success(res.message)
          // 调用getRolesList()查询最新的角色列表信息
          this.getRolesList()
        }).catch((err) => {
          this.$message.error(err)
        })
      }).catch(() => {
        this.$message('您取消了删除！')
      })
    },
    // 分配权限对话框中[确定]按扭的点击事件
    saveAssignMenu() {
      // 打印在[分配权限]对话框中选中的权限节点列表
      console.log(this.$refs.assignRightRef.getCheckedKeys())
      const checkedKeys = this.$refs.assignRightRef.getCheckedKeys() // 全选状态的key
      const parentKeys = this.$refs.assignRightRef.getHalfCheckedKeys() // 半选状态的key
      let strArr = checkedKeys.concat(parentKeys).toString(',') // strArr格式为 "2,3"
      let mid = []
      if (strArr !== null) {
        strArr = strArr.split(',') // 分割成字符串数组
        // 将字符串数组转换成整型数组
        mid = strArr.map(item => {
          return +item
        })
      }
      // 将分配权限对话框给隐藏出来
      this.assignRightVisible = false
      // 封装请求参数
      const param = { 'rid': this.rid, 'mid': mid }
      assignMenu(param).then((res) => {
        if (res.code !== 200) return this.$message.error(res.message)
        this.$message.success(res.message)

        // 为角色分配权限成功后，获取最新的角色列表数据
        this.getRolesList()
      }).catch((err) => {
        console.log(err)
        this.$message.error('分配权限出现错误!')
      })
    },
    // [分配权限]按扭的点击事件
    assignRightToRole(row) {
      this.rid = row.id
      // 将分配权限对话框给显示出来
      this.assignRightVisible = true
      // 再将分配权限对话框中树状权限列表给查询出来
      getAllMenuList().then((res) => {
        // 将查询出来的权限列表赋值给treeRightList
        this.treeRightList = res
      }).catch(() => {
        return this.$message.error('操作失败')
      })

      // 再根据角色ID查询当前角色所拥有的权限列表，格式为 [101,102,104,105]
      // 实现默认选中当前角色所拥有的权限节点
      // 先清空之前选中树形权限节点(如果没有写this.$nextTick()而直接调用this.$refs.assignRightRef可能会报错this.$refs.assignRightRef is undefined，因为DOM还没有更新完)
      this.$nextTick(function() {
        // DOM更新完毕会才会执行this.$nextTick函数
        this.$refs.assignRightRef.setCheckedKeys([])
      })
      // getMenuIdsByRoleId实现通过角色id查询当前角色所拥有权限id集合(格式为以逗号分隔的字符串)
      getMenuIdsByRoleId(row.id).then((res) => {
        let intArr = [] // 用来保存权限id集合在分割成字符串数组并转换成整型后的数组
        if (res !== null) {
          const strArr = res.split(',') // 分割成字符串数组
          // 将字符串数组转换成整型数组
          intArr = strArr.map(item => {
            return +item
          })
        }
        // 从查询出来的权限id集合(格式为以逗号分隔的字符串)，再赋值给defaultCheckedKeys
        // 实现默认选中当前角色所拥有的权限节点
        this.defaultCheckedKeys = res !== null ? intArr : []
        // 解决问题： 在使用【default-checked-keys】时，父节点是选中状态，不管子级节点是否全部被勾选，它默认就都给你勾选上。
        // 参考帖子： https://www.cnblogs.com/e0yu/p/15148102.html
        const newArr = [] // 用来保存被勾选的权限id数组(即第三层权限id数组)
        if (this.defaultCheckedKeys && this.defaultCheckedKeys.length !== 0) {
          // 对defaultCheckedKeys默认勾选权限id数组进行遍历
          this.defaultCheckedKeys.forEach(item => {
            this.checked(item, this.treeRightList, newArr)
          })
          this.defaultCheckedKeys = newArr
        }
      }).catch((err) => {
        console.log(err)
        return this.$message.error('操作失败')
      })
    },
    // 默认el-tree勾选节点是从父级节点开始再到各层的子级节点，造成的问题是当父级节点被勾选时，
    // 不管子级节点是否全部被勾选，它默认就都给你勾选上。
    // 而下面的checked()的原因是与默认的相反，它是从子节点开始再到各层的父级节点，即默认先勾选子节点
    // 只有当所有子节点都勾选上了，才会把当前这些所有子节点的父级节点勾选上，只要有一个子节点没有被勾选
    // 上，父节点是半勾选状态。
    checked(id, data, newArr) {
      data.forEach(item => {
        if (item.id === id) {
          // item.children.length === 0 表示item.children没有数据了，即当前节点为最后的子节点
          if (item.children && item.children.length === 0) {
            // 如果当前节点是最后的子节点就把它添加到newArr数组中
            newArr.push(item.id)
          }
        } else {
          // 如果当前节点不是最后一层子节点(可能是第一层或第二层)，那么再调用checked()进行递归
          // 直到当前节点为最后一层子节点，那么就把它加入到newArr数组中
          if (item.children != null && item.children.length !== 0) {
            this.checked(id, item.children, newArr)
          }
        }
      })
    },
    // 编辑按扭的点击事件
    showEditDialog(row) {
      // 重置role角色对象
      this.resetRole()
      // 显示编辑/添加角色对话框
      this.dialogFormVisible = true
      // 动态修改[编辑对话框窗口标题]
      this.dialogStatus = 'update'
      // row.id为要修改的角色id
      // 发送请求通过角色ID查询角色详情对象信息
      findByRoleId(row.id).then((res) => {
        this.role = res
      }).catch((err) => {
        this.$message.error(err)
      })
    },
    // 修改对话框弹出窗口里[确认]按扭的点击事件
    updateData() {
      updateRole(this.role).then((res) => {
        if (res.code === 200) {
          // 是修改成功
          this.$message.success(res.message)
          // 将[修改操作员弹出对话框窗口]给关闭掉
          this.dialogFormVisible = false
          // 修改成功后，还需要再调用getRolesList()获取最新的角色列表信息
          this.getRolesList()
        } else {
          this.$message.error(res.message)
        }
      }).catch((err) => {
        this.$message.error(err)
      })
    },
    // [取消]按扭的点击事件
    cancelEvent() {
      this.resetRole() // 重置Role表单对象
      this.dialogFormVisible = false
    },
    // 添加角色对话框中的[确认]按扭的点击事件
    insertData() {
      this.$refs['dataFormRef'].validate((valid) => {
        if (valid) {
        // 调用role.js中的saveRole方法发送请求到后端实现保存新增的角色信息
          saveRole(this.role).then((res) => {
          // false后隐藏添加操作员对话框
            this.dialogFormVisible = false
            if (res.code === 200) {
            // 新增成功后，再刷新角色列表数据
              this.getRolesList()
              this.$message({
                title: '提示',
                message: res.message,
                type: 'success',
                duration: 2000
              })
            } else {
              this.$message({
                title: '提示',
                message: res.message,
                type: 'error',
                duration: 2000
              })
            }
          }).catch((err) => {
            console.log(err)
          })
        }
      })
    },
    // 重置Role对象
    resetRole() {
      this.role = {
        id: undefined,
        name: '',
        nameZh: ''
      }
    },
    // 添加角色按扭的点击事件
    openRoleDialog() {
      this.resetRole() // 重置Role表单对象
      this.dialogStatus = 'create'
      this.dialogFormVisible = true
    },
    // 根据角色ID删除对应的权限
    // role为角色对象
    // id为当前需要进行删除的权限ID
    removeRoleThreeRight(role, id) {
      this.$confirm(
        '此操作将永久删除该信息，是否继续?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
        const param = { roleId: role.id, menuId: id }
        delMenuByRoleId(param).then((res) => {
          if (res.code === 200) {
            this.$message.success(res.message)
            // 成功后把当前角色最新的权限列表进行返回，之后赋值给角色对象的children，实现异步刷新角色理的权限列表
            role.children = res.data
          }
        }).catch((err) => {
          this.$message.error('删除权限失败！')
        })
      }).catch(() => {
        return this.$message.info('您取消了删除！')
      })
    },
    // 排序
    sortChange(data) {
      console.log(data)
      // data.prop为排序字段，这里是id。 data.order为点击上下箭头的值，
      // 如果点击下箭头val.order的值为descending，反之为ascending
      const { prop, order } = data
      if (prop === 'id') {
        this.sortByID(order)
      }
    },
    sortByID(order) {
      if (order === 'ascending') {
        this.queryInfo.sort = '+id'
      } else {
        this.queryInfo.sort = '-id'
      }
      // 当前页码设置为1
      this.queryInfo.currPageNo = 1
      // 传到后端时this.currPageNo.sort的值为+id或为-id
      this.getRolesList()
    },
    // 按角色的英文名或中文名模糊查询角色的分页列表信息
    getRolesList() {
      // true时，表格中就会出现正在加载图标
      this.listLoading = true
      pageRoleList(this.queryInfo).then((res) => {
        // false时，正在加载图标就会消失
        this.listLoading = false
        this.roleList = res.data.list
        // 获取总记录数
        this.totalCount = res.data.totalCount
      }).catch((err) => {
        console.log(err)
        this.$message({
          title: '提示',
          message: '查询角色列表失败!',
          type: 'error',
          duration: 2000
        })
      })
    }
  }
}
</script>
<style scoped>
.el-tag{
  margin: 7px;
}
.border-top{
  /*添加上边框线*/
  border-top:1px solid #eee;
}
.border-bottom{
  /*添加下边框线 */
  border-bottom:1px solid #eee;
  border-left: 1px solid #eee;
  border-right: 1px solid #eee;
}
  .vcenter{
    display: flex;
    align-items: center;
  }
</style>
